﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;
using Windows.Foundation.Collections;

namespace XiaoweiUWP.Services
{
    public class AppServiceManager
    {
        private static readonly Lazy<AppServiceManager> LazyInstance = new Lazy<AppServiceManager>(() =>
        {
            return new AppServiceManager();
        }, LazyThreadSafetyMode.ExecutionAndPublication);

        public static AppServiceManager Instance { get { return LazyInstance.Value; } }

        AppServiceConnection Connection { get; set; }

        private bool appServiceEnable = false;
        private object appServiceEnableLock = new object();
        public bool AppServiceEnable
        {
            get
            {
                return appServiceEnable;
            }
            set
            {
                lock (appServiceEnableLock)
                {
                    if (appServiceEnable != value)
                    {
                        appServiceEnable = value;
                        if (value)
                        {
                            CreateSendMessageTask();
                        }
                        else
                        {
                            CloseAppService();
                        }
                    }

                }
            }
        }

        public async Task ReOpenAppService()
        {
            await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
        }

        private void CloseAppService()
        {
            DisposeConnection();
            while (messagesNeedSend.TryDequeue(out SendMessageTaskAwaitor awaitor))
            {
                awaitor.TaskAwaitor.Set();
            }
            AppServiceConnectedEvent.Set();
            HasMessageEvent.Set();
            App.Current.Exit();
        }

        private void DisposeConnection()
        {
            AppServiceConnectedEvent.Reset();
            Connection?.Dispose();
            Connection = null;
        }

        public void HandleBackgroundTask(BackgroundActivatedEventArgs args)
        {
            DisposeConnection();
            AppServiceEnable = true;
            if (m_appServiceDeferral != null) return;
            if (args == null) return;
            Logger.Debug.WriteLine("AppService.");
            m_appServiceDeferral = args.TaskInstance.GetDeferral();
            var details = args.TaskInstance.TriggerDetails as AppServiceTriggerDetails;
            if (!AppServiceEnable)
            {
                m_appServiceDeferral.Complete();
                details.AppServiceConnection.Dispose();
                Logger.Debug.WriteLine("ConnectionManager.HandleBackgroundTask:Connection not enabled,please call StartBackgroundProgress to set Enabled true.");
            }
            AttachTo(details.AppServiceConnection);
            args.TaskInstance.Canceled += OnAppServiceTaskCanceled;
        }

        private void AttachTo(AppServiceConnection connection)
        {
            Connection = connection;
            if (Connection != null)
            {
                Connection.RequestReceived += Connection_RequestReceived;
                Connection.ServiceClosed += Connection_ServiceClosed;
                AppServiceConnectedEvent.Set();
            }
            else
            {
                Logger.Debug.WriteLine("App serivce connection is null");
            }
        }

        private void Connection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
        {
            var deferral=args.GetDeferral();
            if (args.Request.Message == null)
            {
                deferral.Complete();
                return; 
            }
            if(!FunctionDictionary.ContainsKey(args.Request.Message["Request"].ToString()))
            {
                deferral.Complete();
                return;
            }
            ValueSet result = new ValueSet();
            try
            {
                result = FunctionDictionary[args.Request.Message["Request"].ToString()](args.Request.Message);
            }
            catch (Exception ex)
            {
                Logger.Debug.WriteLine($"AppServiceManager Connection_RequestReceived Exception{ex}");
            }
            _=args.Request.SendResponseAsync(result);
            deferral.Complete();
            return;
        }

        private void Connection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
        {
            AppServiceEnable = false;
        }

        private void OnAppServiceTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
        {
            AppServiceEnable = false;
        }

        private object messagesNeedSendLock = new object();

        private Queue<SendMessageTaskAwaitor> messagesNeedSend = new Queue<SendMessageTaskAwaitor>();

        private ManualResetEvent AppServiceConnectedEvent = new ManualResetEvent(false);
        private ManualResetEvent HasMessageEvent = new ManualResetEvent(false);
        private BackgroundTaskDeferral m_appServiceDeferral;

        public Task<ValueSet> SendMessage(ValueSet msg)
        {
            if (!appServiceEnable) return Task.Run(() => { return new ValueSet(); }); ;
            lock (messagesNeedSendLock)
            {
                SendMessageTaskAwaitor sendMessageTaskAwaitor = new SendMessageTaskAwaitor();
                ManualResetEvent TaskAwaitor = new ManualResetEvent(false);

                sendMessageTaskAwaitor.TaskAwaitor = TaskAwaitor;
                sendMessageTaskAwaitor.Message = msg;
                messagesNeedSend.Enqueue(sendMessageTaskAwaitor);
                HasMessageEvent.Set();
                return Task.Run(() =>
                {
                    SendMessageTaskAwaitor sendMessageTaskAwaitor2 = sendMessageTaskAwaitor;
                    TaskAwaitor.WaitOne();
                    TaskAwaitor.Dispose();
                    return sendMessageTaskAwaitor2.Result;
                });
            }
        }



        private void CreateSendMessageTask()
        {
            Task.Run(
                () =>
                {
                    while (appServiceEnable)
                    {
                        AppServiceConnectedEvent.WaitOne();
                        HasMessageEvent.WaitOne();
                        lock (messagesNeedSendLock)
                        {
                            while (messagesNeedSend.TryPeek(out SendMessageTaskAwaitor awaitor))
                            {
                                if (Connection == null) break;
                                try
                                {
                                    var appServiceAwaitor = HP.UtilsLib.TaskAwaiterHelper.GetTaskAwaiter(
                                        async () => {
                                            return await Connection.SendMessageAsync(awaitor.Message);
                                        }
                                        );
                                    appServiceAwaitor.Await();
                                    var response = appServiceAwaitor.GetResult();
                                    appServiceAwaitor.Dispose();
                                    if (response.Status == AppServiceResponseStatus.Success)
                                    {
                                        awaitor.Result = response.Message;
                                        awaitor.TaskAwaitor.Set();
                                    }
                                    messagesNeedSend.Dequeue();

                                }
                                catch (Exception)
                                {
                                    break;
                                }
                            }
                            if (messagesNeedSend.Count == 0)
                            {
                                HasMessageEvent.Reset();
                            }

                        }


                    }
                }
                );

        }
        class SendMessageTaskAwaitor
        {
            public ValueSet Result { get; set; }
            public ValueSet Message { get; set; }
            public ManualResetEvent TaskAwaitor { get; set; }
        }

        private Dictionary<string, Func<ValueSet, ValueSet>> FunctionDictionary = new Dictionary<string, Func<ValueSet, ValueSet>>();
        public void RegisterFunction(string request, Func<ValueSet, ValueSet> func)
        {
            FunctionDictionary[request] = func;
        }
    }
}
